using System;
using System.Windows.Forms;

using Utilities;

namespace GPX
{
    public partial class ElevationAdjustmentForm : FormEx
    {
        private TrackSegment _trackSegment;
        private TrackControl _trackControl;
        private bool _selectingStart;
        private bool _selectingEnd;

        private System.Resources.ResourceManager ResourceManager
        {
            get { return GPXAppResourceManager.ResourceManager; }
        }

        private ElevationAdjustmentForm() { InitializeComponent(); }
        public ElevationAdjustmentForm(TrackSegment trackSegment, TrackControl trackControl)
        {
            InitializeComponent();
            Localizer.LocalizeControl(this, GPXAppResourceManager.ResourceManager);
            _trackSegment = trackSegment;
            _trackControl = trackControl;
            _selectingStart = false;
            _selectingEnd = false;
            _trackControl.WayPointSelected += new TrackControl.WayPointSelectedEventHandler(_trackControl_WayPointSelected);
            nudFrom.Maximum =
                nudTo.Maximum =
                trackSegment.WayPoints.Count;
            nudTo.Value = trackSegment.WayPoints.Count;
            //select first way point; not nice...
            nudFrom_ValueChanged(null, null);
        }

        private void _trackControl_WayPointSelected(object sender, WayPointSelectedEventArgs wpArgs)
        {
            if (wpArgs != null)
            {
                if (wpArgs.SelectedWaypoint != null)
                {
                    int index = wpArgs.SelectedWaypoint.Index;
                    decimal elevation = wpArgs.SelectedWaypoint.Elevation;
                    DateTime dt = wpArgs.SelectedWaypoint.Time;

                    if (_selectingStart)
                    {
                        nudFrom.Value = index;
                        txtTimeFrom.Text = dt.ToShortTimeString();
                        txtElevationFrom.Text = elevation.ToString();
                        _selectingStart = false;
                    }
                    else if (_selectingEnd)
                    {
                        nudTo.Value = index;
                        txtTimeTo.Text = dt.ToShortTimeString();
                        txtElevationTo.Text = elevation.ToString();
                        _selectingEnd = false;
                    }
                }
            }
        }

        private void btnFrom_Click(object sender, EventArgs e)
        {
            _selectingStart = true;
        }

        private void btnTo_Click(object sender, EventArgs e)
        {
            _selectingEnd = true;
        }

        private void btnCancel_Click(object sender, EventArgs e)
        {
            this.DialogResult = DialogResult.Cancel;
            this.Close();
        }

        private void ElevationAdjustmentForm_FormClosing(object sender, FormClosingEventArgs e)
        {
            try
            {
                if (_trackControl != null)
                    _trackControl.WayPointSelected -= new TrackControl.WayPointSelectedEventHandler(_trackControl_WayPointSelected);
            }
            catch (Exception ex)
            {
                System.Diagnostics.Debug.WriteLine("Error:" + ex.ToString());
            }
        }

        private void nudFrom_ValueChanged(object sender, EventArgs e)
        {
            try
            {
                WayPoint wp = _trackSegment.GetWayPointByIndex(Convert.ToInt32(nudFrom.Value));
                if (wp == null)
                {
                    txtTimeFrom.Text = "";
                    txtElevationFrom.Text = "";
                }
                else
                {
                    txtTimeFrom.Text = wp.Time.ToShortTimeString();
                    txtElevationFrom.Text = wp.Elevation.ToString();
                }
            }
            catch (Exception ex)
            {
                System.Diagnostics.Debug.WriteLine("Error:" + ex.ToString());
            }
        }

        private void nudTo_ValueChanged(object sender, EventArgs e)
        {
            try
            {
                WayPoint wp = _trackSegment.GetWayPointByIndex(Convert.ToInt32(nudTo.Value));
                if (wp == null)
                {
                    txtTimeTo.Text = "";
                    txtElevationTo.Text = "";
                }
                else
                {
                    txtTimeTo.Text = wp.Time.ToShortTimeString();
                    txtElevationTo.Text = wp.Elevation.ToString();
                }
            }
            catch (Exception ex)
            {
                System.Diagnostics.Debug.WriteLine("Error:" + ex.ToString());
            }
        }

        private void btnOK_Click(object sender, EventArgs e)
        {
            try
            {
                if (txtElevationFrom.Text == "")
                {
                    //GPX_ElevationAdjustmentForm_btnOK_Click0=Select a start point.
                    MessageBox.Show(ResourceManager.GetString("GPX_ElevationAdjustmentForm_btnOK_Click0"));
                    return;
                }

                if (txtElevationTo.Text == "")
                {
                    //GPX_ElevationAdjustmentForm_btnOK_Click1=Select an end point.
                    MessageBox.Show(ResourceManager.GetString("GPX_ElevationAdjustmentForm_btnOK_Click1"));
                    return;
                }

                double correctedStartElevation;
                if (!double.TryParse(txtElevationStart.Text, out correctedStartElevation))
                {
                    //GPX_ElevationAdjustmentForm_btnOK_Click2=The value for the corrected elevation at the start point must be numeric.
                    MessageBox.Show(ResourceManager.GetString("GPX_ElevationAdjustmentForm_btnOK_Click2"));
                    return;
                }

                double correctedEndElevation;
                if (!double.TryParse(txtElevationEnd.Text, out correctedEndElevation))
                {
                    //GPX_ElevationAdjustmentForm_btnOK_Click3=The value for the corrected elevation at the end point must be numeric.
                    MessageBox.Show(ResourceManager.GetString("GPX_ElevationAdjustmentForm_btnOK_Click3"));
                    return;
                }

                //if the value for temperature adjustment is nonsense, we set it to 1.
                double temperatureAdjustment = 1.0;
                double.TryParse(txtFactor.Text, out temperatureAdjustment);
                double temperature = 15;
                double.TryParse(txtTemperature.Text, out temperature);
                if (rbtTemperature.Checked)
                    temperatureAdjustment = (273 + temperature) / 288;

                int startIndex = Convert.ToInt32(nudFrom.Value);
                int endIndex = Convert.ToInt32(nudTo.Value);

                if (startIndex >= endIndex)
                {
                    //GPX_ElevationAdjustmentForm_btnOK_Click4=The start must be before the end.
                    MessageBox.Show(ResourceManager.GetString("GPX_ElevationAdjustmentForm_btnOK_Click4"));
                    return;
                }

                WayPoint startPoint = _trackSegment.GetWayPointByIndex(startIndex);
                WayPoint endPoint = _trackSegment.GetWayPointByIndex(endIndex);

                //and now do the calculation
                double startElevation = Convert.ToDouble(startPoint.Elevation);
                double elevationDeltaFrom = correctedStartElevation - startElevation;
                double elevationDeltaTo = correctedEndElevation - Convert.ToDouble(endPoint.Elevation);

                //the difference in the above deltas is caused by the temperature adjustment and the general trend,
                //hence the trend is the difference of the deltas corrected for the temperature adjustment.
                double elevationDifferenceReal = correctedEndElevation - correctedStartElevation;
                double elevationDifferenceMeasured = Convert.ToDouble(endPoint.Elevation - startPoint.Elevation);
                //the difference in elevation to be measured at the given temperature, before adjusting for the general trend in air pressure
                double elevationDifferenceMeasuredWithoutTrend = elevationDifferenceReal / temperatureAdjustment;
                //and now we know the trend for the whole selected region:
                double trend = elevationDifferenceMeasured - elevationDifferenceMeasuredWithoutTrend;
                //it must be related to the type of correction base (time / distance)
                if (rbtDistance.Checked)
                {
                    trend /= (endPoint.GetTrackDistanceFromStart() - startPoint.GetTrackDistanceFromStart());
                }
                else
                {
                    trend /= (endPoint.GetTimeSinceStartSeconds() - startPoint.GetTimeSinceStartSeconds());
                }

                WayPoint wp = startPoint;
                while (wp.Index <= endIndex)
                {
                    //step 1: correct for the general trend
                    double diff;
                    if (rbtDistance.Checked)
                    {
                        diff = (startPoint.GetDistanceFromStart().GetLength(startPoint.Latitude) - wp.GetDistanceFromStart().GetLength(wp.Latitude)) * trend;
                    }
                    else
                    {
                        diff = (startPoint.GetTimeSinceStartSeconds() - wp.GetTimeSinceStartSeconds()) * trend;
                    }
                    wp.AdjustPoint(0, 0, diff);

                    //step 2: adjust for temperature dependence, and the overall adjustment
                    diff = (Convert.ToDouble(wp.Elevation) - startElevation) * (temperatureAdjustment - 1);
                    diff += elevationDeltaFrom;
                    wp.AdjustPoint(0, 0, diff);

                    wp = wp.NextWaypoint;
                    if (wp == null)
                        break;
                }

                this.DialogResult = DialogResult.OK;
                this.Close();
            }
            catch (Exception ex)
            {
                Utils.HandleException(ex);
            }
        }
    }
}
